// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

// ************************************************************************* //
//  File: avtCoordSwapFilter.C
// ************************************************************************* //

#include <avtCoordSwapFilter.h>

#include <vtkCellData.h>
#include <vtkPointData.h>
#include <vtkPointSet.h>
#include <vtkRectilinearGrid.h>
#include <vtkIntArray.h>


static void SwapExtentsCallback(const double *, double *, void *);


// ****************************************************************************
//  Method: avtCoordSwapFilter constructor
//
//  Programmer: childs -- generated by xml2avt
//  Creation:   Wed Feb 2 15:47:40 PST 2005
//
// ****************************************************************************

avtCoordSwapFilter::avtCoordSwapFilter()
{
}


// ****************************************************************************
//  Method: avtCoordSwapFilter destructor
//
//  Programmer: childs -- generated by xml2avt
//  Creation:   Wed Feb 2 15:47:40 PST 2005
//
//  Modifications:
//
// ****************************************************************************

avtCoordSwapFilter::~avtCoordSwapFilter()
{
}


// ****************************************************************************
//  Method:  avtCoordSwapFilter::Create
//
//  Programmer: childs -- generated by xml2avt
//  Creation:   Wed Feb 2 15:47:40 PST 2005
//
// ****************************************************************************

avtFilter *
avtCoordSwapFilter::Create()
{
    return new avtCoordSwapFilter();
}


// ****************************************************************************
//  Method:      avtCoordSwapFilter::SetAtts
//
//  Purpose:
//      Sets the state of the filter based on the attribute object.
//
//  Arguments:
//      a        The attributes to use.
//
//  Programmer: childs -- generated by xml2avt
//  Creation:   Wed Feb 2 15:47:40 PST 2005
//
// ****************************************************************************

void
avtCoordSwapFilter::SetAtts(const AttributeGroup *a)
{
    atts = *(const CoordSwapAttributes*)a;
}


// ****************************************************************************
//  Method: avtCoordSwapFilter::Equivalent
//
//  Purpose:
//      Returns true if creating a new avtCoordSwapFilter with the given
//      parameters would result in an equivalent avtCoordSwapFilter.
//
//  Programmer: childs -- generated by xml2avt
//  Creation:   Wed Feb 2 15:47:40 PST 2005
//
// ****************************************************************************

bool
avtCoordSwapFilter::Equivalent(const AttributeGroup *a)
{
    return (atts == *(CoordSwapAttributes*)a);
}


// ****************************************************************************
//  Method: avtCoordSwapFilter::ExecuteData
//
//  Purpose:
//      Sends the specified input and output through the CoordSwap filter.
//
//  Arguments:
//      in_dr      The input data representation.
//
//  Returns:       The output data representatoin.
//
//  Programmer: childs -- generated by xml2avt
//  Creation:   Wed Feb 2 15:47:40 PST 2005
//
//  Modifications:
//    Kathleen Biagas, Tue Aug 21 16:06:23 MST 2012
//    Preserve coordinate type.
//
//    Eric Brugger, Wed Jul 23 11:09:55 PDT 2014
//    Modified the class to work with avtDataRepresentation.
//
//    Kathleen Biagas, Thu Jun 4 18:15:22 MST 2015
//    Make sure base_index gets swapped.
//
//    Eric Brugger, Tue Aug 25 10:20:24 PDT 2015
//    Modified the routine to return NULL if the output data set was NULL.
//
// ****************************************************************************

avtDataRepresentation *
avtCoordSwapFilter::ExecuteData(avtDataRepresentation *in_dr)
{
    //
    // Get the VTK data set.
    //
    vtkDataSet *in_ds = in_dr->GetDataVTK();

    int   i, j, k;

    int old_index[3] = { 0, 0, 0 };
    CoordSwapAttributes::Coord new_coord[3];
    new_coord[0] = atts.GetNewCoord1();
    new_coord[1] = atts.GetNewCoord2();
    new_coord[2] = atts.GetNewCoord3();

    for (i = 0 ; i < 3 ; i++)
    {
        switch (new_coord[i])
        {
          case CoordSwapAttributes::Coord1:
            old_index[i] = 0;
            break;
          case CoordSwapAttributes::Coord2:
            old_index[i] = 1;
            break;
          case CoordSwapAttributes::Coord3:
            old_index[i] = 2;
            break;
        }
    }

    vtkDataSet *rv = NULL;
    int ds_type = in_ds->GetDataObjectType();
    if (ds_type == VTK_RECTILINEAR_GRID)
    {
        vtkRectilinearGrid *rg = (vtkRectilinearGrid *) in_ds;
        int dims[3];
        rg->GetDimensions(dims);

        vtkRectilinearGrid *out_rg = (vtkRectilinearGrid *) rg->NewInstance();
        vtkDataArray *in_coords[3];
        in_coords[0] = rg->GetXCoordinates();
        in_coords[1] = rg->GetYCoordinates();
        in_coords[2] = rg->GetZCoordinates();
        int new_dims[3];
        new_dims[0] = dims[old_index[0]];
        new_dims[1] = dims[old_index[1]];
        new_dims[2] = dims[old_index[2]];
        out_rg->SetDimensions(new_dims);
        out_rg->SetXCoordinates(in_coords[old_index[0]]);
        out_rg->SetYCoordinates(in_coords[old_index[1]]);
        out_rg->SetZCoordinates(in_coords[old_index[2]]);

        vtkPointData *inPD = rg->GetPointData();
        vtkPointData *outPD = out_rg->GetPointData();
        outPD->CopyAllocate(inPD);
        for (i = 0 ; i < dims[0] ; i++)
            for (j = 0 ; j < dims[1] ; j++)
                for (k = 0 ; k < dims[2] ; k++)
                {
                    int old_pt_index = k*dims[0]*dims[1] + j*dims[0] + i;
                    int vals[3] = { i, j, k };
                    int nv[3];
                    nv[0] = vals[old_index[0]];
                    nv[1] = vals[old_index[1]];
                    nv[2] = vals[old_index[2]];
                    int new_pt_index = nv[2]*new_dims[0]*new_dims[1] + 
                                    nv[1]*new_dims[0] + nv[0];
                    outPD->CopyData(inPD, old_pt_index, new_pt_index);
                }
        vtkCellData *inCD = rg->GetCellData();
        vtkCellData *outCD = out_rg->GetCellData();
        outCD->CopyAllocate(inCD);
        int old_cell_dims[3];
        old_cell_dims[0] = (dims[0]-1 <= 0 ? 1 : dims[0]-1);
        old_cell_dims[1] = (dims[1]-1 <= 0 ? 1 : dims[1]-1);
        old_cell_dims[2] = (dims[2]-1 <= 0 ? 1 : dims[2]-1);
        int new_cell_dims[3];
        new_cell_dims[0] = (new_dims[0]-1 <= 0 ? 1 : new_dims[0]-1);
        new_cell_dims[1] = (new_dims[1]-1 <= 0 ? 1 : new_dims[1]-1);
        new_cell_dims[2] = (new_dims[2]-1 <= 0 ? 1 : new_dims[2]-1);
        for (i = 0 ; i < old_cell_dims[0] ; i++)
            for (j = 0 ; j < old_cell_dims[1] ; j++)
                for (k = 0 ; k < old_cell_dims[2] ; k++)
                {
                    int old_cell_index = k*old_cell_dims[0]*old_cell_dims[1] +
                                         j*old_cell_dims[0] + i;
                    int vals[3] = { i, j, k };
                    int nv[3];
                    nv[0] = vals[old_index[0]];
                    nv[1] = vals[old_index[1]];
                    nv[2] = vals[old_index[2]];
                    int new_cell_index = 
                                    nv[2]*new_cell_dims[0]*new_cell_dims[1] + 
                                    nv[1]*new_cell_dims[0] + nv[0];
                    outCD->CopyData(inCD, old_cell_index, new_cell_index);
                }

        out_rg->GetFieldData()->ShallowCopy(rg->GetFieldData());
        rv = out_rg;
    }
    else if (ds_type == VTK_POLY_DATA || ds_type == VTK_UNSTRUCTURED_GRID ||
             ds_type == VTK_STRUCTURED_GRID)
    {
        vtkPointSet *ps = (vtkPointSet *) in_ds;
        vtkPoints *in_pts = ps->GetPoints();
        int npts = in_pts->GetNumberOfPoints();

        vtkPointSet *out_ps = (vtkPointSet *) ps->NewInstance();
        out_ps->ShallowCopy(ps);
        vtkPoints *out_pts = vtkPoints::New(in_pts->GetDataType());
        out_ps->SetPoints(out_pts);
        out_pts->Delete();
        out_pts->SetNumberOfPoints(npts);

        for (i = 0 ; i < npts ; i++)
        {
            double pt[3], tmp[3];
            in_pts->GetPoint(i, pt);
            tmp[0] = pt[old_index[0]];
            tmp[1] = pt[old_index[1]];
            tmp[2] = pt[old_index[2]];
            out_pts->SetPoint(i, tmp);
        }

        rv = out_ps;
    }

    if (rv != NULL)
    {
        vtkIntArray *bi_array = vtkIntArray::SafeDownCast(
            rv->GetFieldData()->GetArray("base_index"));
        if (bi_array)
        {
            int orig_bi_vals[3]= {bi_array->GetValue(0), bi_array->GetValue(1),
                                  bi_array->GetValue(2)};
            // Need a new array here, so we don't affect things upstream.
            vtkIntArray *new_bi = vtkIntArray::New();
            new_bi->SetNumberOfComponents(1);
            new_bi->SetNumberOfTuples(3);
            new_bi->SetName("base_index");
            for (int i = 0 ; i < 3 ; i++)
            {
                switch (new_coord[i])
                {
                case CoordSwapAttributes::Coord1:
                    new_bi->SetValue(i, orig_bi_vals[0]);
                    break;
                case CoordSwapAttributes::Coord2:
                    new_bi->SetValue(i, orig_bi_vals[1]);
                    break;
                case CoordSwapAttributes::Coord3:
                    new_bi->SetValue(i, orig_bi_vals[2]);
                    break;
                }
            }
            rv->GetFieldData()->RemoveArray("base_index");
            rv->GetFieldData()->AddArray(new_bi);
            new_bi->Delete();
        }
    }

    avtDataRepresentation *out_dr = NULL;
    if (rv != NULL)
    {
        out_dr = new avtDataRepresentation(rv,
            in_dr->GetDomain(), in_dr->GetLabel());
        rv->Delete();
    }

    return out_dr;
}


// ****************************************************************************
//  Method: avtCoordSwapFilter::UpdateDataObjectInfo
//
//  Purpose:
//    Indicates that the coordinates were swapped.
//
//  Programmer: Hank Childs
//  Creation:   February 3, 2005
//
//  Modifications:
//    Brad Whitlock, Mon Apr  7 15:55:02 PDT 2014
//    Add filter metadata used in export.
//    Work partially supported by DOE Grant SC0007548.
//
// ****************************************************************************

void
avtCoordSwapFilter::UpdateDataObjectInfo(void)
{
    avtDataAttributes &in_atts  = GetInput()->GetInfo().GetAttributes();
    avtDataAttributes &out_atts = GetOutput()->GetInfo().GetAttributes();

    //
    // This operator invalidates any transform matrix in the pipeline.
    //
    GetOutput()->GetInfo().GetValidity().SetPointsWereTransformed(true);
    out_atts.SetCanUseTransform(false);
    out_atts.SetCanUseInvTransform(false);

    in_atts.TransformSpatialExtents(out_atts, SwapExtentsCallback, this);

    out_atts.AddFilterMetaData("CoordSwap");
}


// ****************************************************************************
//  Function: SwapExtentsCallback
//
//  Purpose:
//      A callback that can feed the swap extents call to the coordswap filter.
//
//  Programmer: Hank Childs
//  Creation:   February 3, 2005
//
// ****************************************************************************

void
SwapExtentsCallback(const double *in_exts, double *out_exts, void *f)
{
     avtCoordSwapFilter *csf = (avtCoordSwapFilter *) f;
     csf->SwapExtents(in_exts, out_exts);
}


// ****************************************************************************
//  Method: avtCoordSwapFilter::SwapExtents
//
//  Purpose:
//      Swaps some extents.
//
//  Programmer: Hank Childs
//  Creation:   February 3, 2005
//
// ****************************************************************************

void
avtCoordSwapFilter::SwapExtents(const double *in_exts, double *out_exts)
{
    int old_index[3] = { 0, 0, 0 };
    CoordSwapAttributes::Coord new_coord[3];
    new_coord[0] = atts.GetNewCoord1();
    new_coord[1] = atts.GetNewCoord2();
    new_coord[2] = atts.GetNewCoord3();

    for (int i = 0 ; i < 3 ; i++)
    {
        switch (new_coord[i])
        {
          case CoordSwapAttributes::Coord1:
            old_index[i] = 0;
            break;
          case CoordSwapAttributes::Coord2:
            old_index[i] = 1;
            break;
          case CoordSwapAttributes::Coord3:
            old_index[i] = 2;
            break;
        }
    }

    out_exts[0] = in_exts[2*old_index[0]];
    out_exts[1] = in_exts[2*old_index[0]+1];
    out_exts[2] = in_exts[2*old_index[1]];
    out_exts[3] = in_exts[2*old_index[1]+1];
    if (GetInput()->GetInfo().GetAttributes().GetSpatialDimension() > 2)
    {
        out_exts[4] = in_exts[2*old_index[2]];
        out_exts[5] = in_exts[2*old_index[2]+1];
    }
}


